이 노트북은 Hell, Tensorflow! 의 내용을 참고로 하여 노트북으로 재 정돈한 것입니다.
In [1]:
%load_ext watermark
%watermark -vm -p tensorflow,numpy,scikit-learn
In [2]:
import tensorflow as tf
텐서플로우의 디폴트 그래프는 직접 접근을 할 수 없고 get_default_graph
메소드를 이용합니다.
In [3]:
graph = tf.get_default_graph()
초기에는 디폴트 그래프에 아무런 연산도 들어 있지 않고 비어 있습니다.
In [4]:
graph.get_operations()
Out[4]:
실수 1.0 값을 가지는 상수 input_value
를 만듭니다. name
옵션을 사용하여 이름을 지정하면 텐서보드의 그래프에서 구분하기 편리합니다.
In [5]:
input_value = tf.constant(1.0, name='input_value')
상수 하나도 텐서플로우 그래프에서는 하나의 노드로 취급되어 get_operations
에서 리턴되는 리스트가 비어있지 않습니다.
In [6]:
graph.get_operations()
Out[6]:
get_operations
로 받은 리스트에는 하나의 엘리먼트가 들어 있고 엘리먼트는 Operation
클래스의 인스턴스입니다.
In [7]:
ops = graph.get_operations()
len(ops), ops[0].__class__
Out[7]:
ops
의 첫번째 노드(여기서는 상수노드)의 정의를 조회하면 프로토콜 버퍼 형식으로 연산 노드를 표현하고 있음을 알 수 있습니다.
In [8]:
op = ops[0]
op.node_def
Out[8]:
input_value
는 상수 텐서를 위한 일종의 연산 노드이며 값이 들어 있지 않습니다.
In [9]:
input_value.__class__, input_value
Out[9]:
텐서플로우의 세션을 생성한 후에 input_value
를 실행하면 결과 값이 리턴됩니다.
In [10]:
sess = tf.Session()
sess.run(input_value)
Out[10]:
두 수를 곱하는 연산을 만들어 보기 위해 weight
변수를 만듭니다. 상수 노드는 tensorflow.python.framework.ops.Tensor
의 객체인데 변수 노드는 tensorflow.python.ops.variables.Variable
의 객체입니다.
In [11]:
weight = tf.Variable(0.8, name='weight')
weight
Out[11]:
이제 연산 노드는 다섯 개로 늘어납니다. 변수 텐서를 생성하면 그래프에 초기값과 할당, 조회에 관련된 노드가 추가로 생성되기 때문입니다.
In [12]:
ops = graph.get_operations()
for op in ops:
print(op.name)
weight
변수와 input_value
상수를 곱하여 곱셈 노드로 만들어지는 output_value
텐서를 만듭니다.
In [13]:
output_value = weight * input_value
output_value
Out[13]:
그래프의 노드를 다시 조회하면 mul
노드가 추가된 것을 확인할 수 있습니다.
In [14]:
ops = graph.get_operations()
for op in ops:
print(op.name)
그래프의 모든 변수를 초기화하기 위해 init
노드를 만들고 run
메소드로 실행합니다.
In [15]:
init = tf.initialize_all_variables()
sess.run(init)
1 * 0.8 의 출력 값은 예상대로 0.8을 리턴합니다.
In [16]:
sess.run(output_value)
Out[16]:
SummaryWriter
를 사용하여 log_simple_graph
디렉토리에 sess
에서 만들어진 세션 그래프 정보를 기록합니다.
In [17]:
summary_writer = tf.train.SummaryWriter('log_simple_graph', sess.graph)
쉘에서 tensorboard --logdir=log_simple_graph
명령으로 텐서보드를 실행하고 브라우저로 http://localhost:6006
으로 접속한 후 그래프 탭을 클릭하면 아래와 같은 그림이 보입니다. 이 그래프는 init
연산에서 weight
변수를 초기화하고 mul
연산에서 input_value
와 wegiht
를 사용하고 있다는 것을 잘 보여 주고 있습니다.
같은 값을 가지는 상수와 변수를 x, w 로 다시 만듭니다. 그리고 곱셉 노드를 파이썬의 곱셉 연산자를 사용하지 않고 텐서플로우에서 제공하는 수학 함수인 mul
을 사용하여 표현할 수 있습니다.
In [18]:
x = tf.constant(1.0, name='x')
w = tf.Variable(0.8, name='w')
y = tf.mul(w, x, name='y')
In [19]:
init = tf.initialize_all_variables()
sess.run(init)
실제 참 값(y_)을 0이라고 정의하고 예측값과의 차이의 제곱을 에러 함수(loss function or error function)로 정의합니다.
In [20]:
y_ = tf.constant(0.0)
loss = (y - y_)**2
학습속도를 0.025로 하고 그래디언트 디센트 최적화 방식을 선택합니다.
In [21]:
optim = tf.train.GradientDescentOptimizer(learning_rate=0.025)
grads_and_vars = optim.compute_gradients(loss)
In [22]:
grads_and_vars
Out[22]:
수동으로 에러함수의 기울기를 계산해 보면 아래와 같이 1.6이 나옵니다. 가중치 값이 0.8 이므로 y = 0.8 * 1.0 = 0.8
이 되고 위에서 가정한 대로 y_ = 0
입니다. 에러 함수의 미분 방정식은 2(y - y_)
이므로 결과 값은 2 * 0.8 = 1.6
이 됩니다.
In [23]:
sess.run(grads_and_vars[1][0])
Out[23]:
계산된 그래디언트를 가중치에 반영하면 학습속도가 0.025 이므로 0.025 * 1.6 = 0.04
가 되어 w 가 0.04 만큼 감소됩니다.
In [24]:
sess.run(optim.apply_gradients(grads_and_vars))
In [25]:
sess.run(w)
Out[25]:
이 과정을 반복하도록 루프를 작성하고 summary_writer
를 이용하여 결과 y 값을 기록하면 텐서보드에서 그래프로 값의 변화를 확인할 수 있습니다.
In [26]:
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)
summary_y = tf.scalar_summary('output', y)
for i in range(100):
summary_str = sess.run(summary_y)
summary_writer.add_summary(summary_str, i)
sess.run(train_step)
결과 그래프에서 예측 값 y 가 0 으로 수렴하고 있는 과정을 볼 수 있습니다.